"""
api自动化项目实战.py
1. config/api_conf.py - 配置文件
作用：存储项目全局配置信息（路径、URL、数据库配置等）
    复制 api_conf.py config包中
    import os
    # __file__ 是当前文件名, os.path.dirname可以获取指定文件的 存放路径
    BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # 动态获取项目根目录路径
    # 定义日志和Allure报告路径
    API_LOG_DIR = os.path.join(BASE_DIR,r"log\api_log") # 日志目录
    ALLURE_DIR= os.path.join(BASE_DIR,"allure_report") # Allure报告目录
    # API基础配置
    IP="127.0.0.1"
    PORT="8080"
    URL="http://"+IP+":"+PORT # 完整的基础URL
    # 数据库配置
    DB_ENV={
        "username":"root",   # 数据库用户名
        "password":"123456", # 数据库密码
        "host":"192.168.1.2", # 数据库主机地址
        "port":3306,   # 数据库端口
        "dbname":"ranzhi"  # 数据库名称
    }
    # 测试配置是否生效
    if __name__ == '__main__':
        print(os.path.dirname(os.path.dirname(__file__)))  # 项目根目录
        print(os.path.join(BASE_DIR,r"log\api_log"))  # 日志目录
2. common/api_log.py - 日志模块
作用：配置日志文件的路径和文件名
        新建api_log.py , common包中
        from config.api_conf import BASE_DIR,API_LOG_DIR # 引用api_conf.py配置文件
        # 项目根目录下 的 log 文件夹
        path = API_LOG_DIR
        # 创建文件日志的路径文件名
        filename = path + f'/api_log_{daytime}.log'
3. 编写测试用例， 在testcase下创建 testapi包
作用：实现具体的API测试逻辑（参数化版本）
    test_login_api.py
        import pytest
        import requests

        from config.api_conf import URL
        # 读取YAML测试数据
        @pytest.mark.api
        def test_login_bank():
            sess = requests.session()
            url=URL+"/pinter/bank/api/login"
            print(url)
            data={"userName":"admin","password":"123456"}
            rp = sess.request(method="post",url=url,data=data)
            print(rp.text)
    修改 ini 添加 -m="api" ,调试api测试用例。

4.创建yaml文件 api_login_data.yaml
作用：数据驱动测试，存储接口请求参数和预期结果
    -
  request:  # 接口公共配置
    epic: 接口自动化项目
    feature: 用户相关
    story: 登录金融项目登录接口
    method: post
    url: /pinter/bank/api/login
    headers: null
    cookies: null
    parametrize-types: userName,password,status_code, # params,data,json ,files
  parametrize: # 测试用例数据
    - { title: 输入正确用户名和正确密码,登录成功,data: {userName: admin,password: "123456" },expect: {"code":"0","message":"success","data":null},params: null,json: null,files: null}
    - { title: 输入空的用户名和正确密码,登录失败,data: {userName: ,password: "123456" },expect: {'code': '1', 'data': null, 'message': '参数为空'},params: null,json: null,files: null}
    - { title: 输入正确用户名和空的密码,登录失败,data: {userName: admin,password: },expect: {'code': '1', 'data': null, 'message': '参数为空'},params: null,json: null,files: null,}
    - { "title": "输入正确用户名和正确密码,登录成功",data: { "userName": "admin","password": "123456" },expect: {"code":"0","message":"success","data":null},params: null,json: null,files: null }
    - { "title": "输入空的用户名和正确密码,登录失败",data: { "userName": "","password": "123456" },expect: {'code': '1', 'data': null, 'message': '参数为空'} ,params: null,json: null,files: null }
    - { "title": "输入正确用户名和空的密码,登录失败",data: { "userName": "admin","password": "" },expect: {'code': '1', 'data': null, 'message': '参数为空'},params: null,json: null,files: null, }
5. testloign 用例参数化
    import pytest
import requests

from common.yaml_tools import YamlTools
from config.api_conf import URL
login_yaml_data = YamlTools.read_yaml_file('api_login_data.yaml')[0]
print(login_yaml_data)
@pytest.mark.api
@pytest.mark.parametrize("logindata",login_yaml_data["parametrize"])
def test_login_bank(logindata):
    sess = requests.session()

    print(login_yaml_data["request"]["url"])

    url=URL+login_yaml_data["request"]["url"]
    method=login_yaml_data["request"]["method"]

    data=logindata["data"]
    rp = sess.request(method=method,url=url,data=data)
    print(rp.json())
    assert rp.json() == logindata["expect"]
6. 创建一个统一的接口请求 common ，base_request.py
common/base_request.py - 请求基类
作用：封装通用的HTTP请求方法
        import requests
        from common.api_log import log # 导入日志模块
        from common.yaml_tools import YamlTools # 导入配置中的基准URL
        from config.api_conf import URL


        class BaseRequest:
            # 使用类变量保存Session对象，所有实例共享同一个Session
            sess = requests.session()
            @classmethod
            # @classmethod 是 Python 中的一个装饰器（Decorator），用于定义类方法，直接绑定到类本身而不是类的实例
                核心作用 1. 访问类属性: 类方法可以直接操作类级别的属性（ sess 变量），无需依赖实例。
                       2. 共享类状态: 所有实例和类方法共享同一个类属性，适合需要跨实例共享数据的场景（例如维护全局的 Session 对象）。
                       3.统一管理请求逻辑:通过类方法封装通用的请求逻辑，提高代码复用性：


            def send_request(cls,**kwargs):
            发送HTTP请求的核心方法
            # param kwargs: 接收requests.request的所有参数
            # return: Response对象
                try:
                    rp = cls.sess.request(**kwargs)  # 通过Session发送请求（自动处理Cookies） cls.send_request()：通过 cls 调用其他类方法。

                except Exception as e: # 异常处理
                    log.error(e)
                else:
                    return rp
            @classmethod
            def execute_api_request(cls,request,parametrize):
                # 执行接口请求的封装方法
                # request 传入yaml文件的 request部分，接口的通用信息
                # parametrize 传入的 测试用例列表
                # 从request配置中获取基础参数
                method=request["method"]

                url= URL+request["url"] # 构建完整URL
                cookies= request["cookies"]
                headers= request["headers"]
                从parametrize中获取动态参数
                data=parametrize["data"]
                files= parametrize["files"]
                params = parametrize["params"]
                json = parametrize["json"]
                rp = cls.send_request(method=method,url=url,cookies=cookies,headers=headers,data=data,json=json,files=files,params=params)
                print(rp.text,rp.json())
                return rp # 返回
        # 测试用代码
        if __name__ == '__main__':
            login_yaml_data = YamlTools.read_yaml_file('api_login_data.yaml')[0]
            rp = BaseRequest.execute_api_request(request=login_yaml_data["request"],parametrize=login_yaml_data["parametrize"][0])
7. test方法中，使用通用接口进行 接口自动化测试
        登录接口测试：
        import pytest
        import requests
        from common.base_request import BaseRequest
        from common.yaml_tools import YamlTools
        from config.api_conf import URL

        login_yaml_data = YamlTools.read_yaml_file('api_login_data.yaml')[0]

        @pytest.mark.api
        @pytest.mark.parametrize("logindata",login_yaml_data["parametrize"])
        def test_login_bank2(logindata):
            # 使用BaseRequest发送请求
            rp = BaseRequest.execute_api_request(request=login_yaml_data["request"],
            # 断言响应结果                                  parametrize=logindata)
            assert rp.json()==logindata["expect"]

        查询接口： 先写yaml文件
                -
                  request:
                    epic: 接口自动化项目
                    feature: 用户相关
                    story: 登录金融项目查询接口
                    method: get
                    url: /pinter/bank/api/query
                    headers: null
                    cookies: null
                    parametrize-types: userName,password,status_code, # params,data,json ,files
                  parametrize:
                    - { title: 输入正确用户名查询金额查询成功,data: null,expect: success,params: {userName: admin},json: null,files: null}
                    - { title: 输入错误的用户名查询失败,data: null,expect: 用户未登录,params: {userName: aaa},json: null,files: null}
        编写 查询接口测试方法：
            query_yaml_data = YamlTools.read_yaml_file('api_query_data.yaml')[0]

            @pytest.mark.api
            @pytest.mark.parametrize("querydata",query_yaml_data["parametrize"])
            def test_login_bank_query(querydata):
                # 1.银行登录成功 ，传入成功用例
                rp = BaseRequest.execute_api_request(request=login_yaml_data["request"],parametrize=login_yaml_data["parametrize"][0])
                print(rp.json())
                # 银行查询
                rp = BaseRequest.execute_api_request(request=query_yaml_data["request"],
                                                     parametrize=querydata)
                assert rp.json()["message"]==querydata["expect"]
8.allure:
    1.解压 zip 路径不能有中文，找到bin目录所在路径，添加系统环境变量path
    2.在python终端下载 pip install allure-pytest
    3.在pytest.ini文件配置中加入
        --alluredir=./allure-data --clean-alluredir
    作用是：设置创建对象所需的存放路径，设置为执行前自动清理
    4.run.py中添加代码
        os.system("allure generate ./allure-data -o ./reports  --clean")
        生成allure报告

    5.报告优化：
        @allure.epic(项目名称)
        @allure.feature(模块名称)
        @allure.story(接口名称)
        @allure.epic(login_yaml_data["request"]["epic"])
        @allure.feature(login_yaml_data["request"]["feature"])
        @allure.story(login_yaml_data["request"]["story"])
        对统一接口进行优化
        allure.attach(数据,f"对数剧的说明文字"，attachment_type=allure.attachment_type.TEXT)
        allure.attach(method,f"接口请求方式{methon}",attachment_type=allure.attachment_type.TEXT)

"""
